#include "./ackHandlers/rrqAck.h"
#include "clientHandler.h"
#include "./logger/logger.h"
#include "modeHandlers/octetMode.h"
#include "modeHandlers/netasciiMode.h"
#include "modeHandlers/unknowMode.h"
#include "opcodeHandlers/rrqHandler.h"
#include "opcodeHandlers/errorHandler.h"
#include "./clientList/clientList.h"
#include "serverDefines.h"
#include <stdlib.h> /*for malloc,free*/
#include <string.h> /*for memcpy*/
#include <sys/types.h> /*for socket,sendto*/
#include <sys/socket.h> /*for socket,sendto*/
#include <unistd.h> /*for getpid*/
#include <sys/types.h> /*for getpid*/
#include <stdio.h> /*for perror*/
#include <errno.h> /*for perror*/
#include <netinet/in.h> /*for sockadder_in*/
#include <arpa/inet.h> /*for inet_ntoa*/
#include <signal.h>

#define MAXBUFLEN 512
#define MSGNUMBERSIZE 2
#define OPPCODESIZE 2
#define MAXMODESIZE 8
#define REPLYHEADERSIZE() (MSGNUMBERSIZE + OPPCODESIZE)
#define isAcknowledger(msg) ((msg->msgID != ntohs(ackRequest)) && (msg->msgID != ntohs(errRequest)))

/**
 * author: Assaf Paz.
 * 
 * acceptRequest
 * 
 * request - request string that was received
 * size - size of the request string (request string might contain '\0' within)
 * clientAdder - the client (sender of request) network information struct
 * 
 * log that a request was recieved
 */
void acceptRequest(char* request, int size,struct sockaddr_in* clientAdder)
{
    logMessage("acceptRequest: recv %d bytes from %s:%d\n", size, inet_ntoa(clientAdder->sin_addr),ntohs(clientAdder->sin_port));
    char *receivedPacket = (char*) malloc (sizeof(char)*size);
    removeUnprintable(request,size,receivedPacket);
    logMessage("acceptRequest: received packet:<%s>\n",receivedPacket);
    free(receivedPacket);
}

/**
 * author: Haim Tsolker.
 * 
 * examineRequest
 * 
 * requestStruct - request struct to fill from the request string
 * remoteAddr - the client (sender of request) network information struct
 * request - request string
 * 
 * the request string is parsed and requestStruct is filled accordingly
 * with the following items:
 * fileName - actually the request data, in RRQ situation its the filename
 * modeHandler - a pointer to the mode handler function that deal with translation
 *               for each step of the request.
 * opcodeHandler - a pointer to the opcode handler function that executes a single 
 *                 step of the request.
 * acknowledgeHandler - a pointer to the acknowledge function that check a proper
 *                      acknowledgement from the client on each step.
 * remoteAddr - a pointer to the client (sender of request) network information struct 
 */
request_p examineRequest(request_p requestStruct, struct sockaddr_in *remoteAddr , void *request)
{
    struct packed_struct
    {
unsigned int oppCodeHigh:
        8;
unsigned int oppCodeLow:
        8;
unsigned int oppCodeMask:
        32;
    }
    s_pack;
    int selectedOpcode=0;
    char mode[MAXMODESIZE+1];
    memcpy(&selectedOpcode,(int*)request,sizeof(int));
    s_pack.oppCodeMask=0x0000ffff;
    selectedOpcode = s_pack.oppCodeMask & *((int*)request);
    s_pack.oppCodeHigh = selectedOpcode;
    s_pack.oppCodeLow = selectedOpcode>>8;
    memset(&selectedOpcode,0,sizeof(int));
    selectedOpcode=s_pack.oppCodeHigh;
    selectedOpcode<<=8;
    selectedOpcode=s_pack.oppCodeLow;
    strncpy(requestStruct->fileName,(char*)(request+OPPCODESIZE),MAXFILENAMESIZE+1);
    requestStruct->fileName[MAXFILENAMESIZE]='\0';
    strncpy(mode,(char*)(request+OPPCODESIZE+strlen(requestStruct->fileName)+1),MAXMODESIZE);
    mode[MAXMODESIZE]='\0';

    if (wrightRequest == selectedOpcode)
    {
        logMessage("examineRequest: Write request - Opcode is not implemented\n");
        //requestStruct->opcodeHandler=<pointer to function that handle write>
        //requestStruct->acknowledgeHandler=<pointer to function that handle write ack>
    }
    else if (readRequest == selectedOpcode)
    {
        logMessage("examineRequest: Read request\n");
        requestStruct->opcodeHandler=*handleRRQ;
        requestStruct->acknowledgeHandler=*acknowledgePacket;
    }
    else
    {
        logMessage("examineRequest: ERROR - Opcode is not supported\n");
    	requestStruct->opcodeHandler=*handleError;
    }

    if (!strcmp("netascii",mode))
    {
        requestStruct->modeHandler=*handleAscii;
    }
    else if (!strcmp("octet",mode))
    {
        requestStruct->modeHandler=*handleOctet;
    }
    else
    {
        logMessage("examineRequest: ERROR - Mode is not supported\n");
        requestStruct->modeHandler=*NonSupportModeHandler;
    }
    requestStruct->remoteAddr = remoteAddr;
    requestStruct->addionalData = NULL;
    return requestStruct;
}

//client state enumeration: client can be either dead or alive
typedef enum {DEAD,ALIVE} ClientState_e;

//flag that mark the client state
ClientState_e clientFlag = ALIVE;

/**
 * author: Assaf Paz.
 * 
 * handleClientStateChange
 * 
 * client recieved a killing signal - it is now dying
 */
void handleClientStateChange(int s)
{
	clientFlag=DEAD;
	logMessage("client %d state has changed\n",getpid());
}

/**
 * author: Assaf Paz.
 * 
 * executeRequest
 * 
 * request - all the required information for handling a request
 * 
 * the function executes request in steps where is step isas follows: 
 * 1) data is extracted using opcodeHanlder function
 * 2) the data get translated by the modeHandler
 * 3) the translated data is sent to the client 
 * 4) wait for a proper acknowledgement (using acknowledgeHandler)
 * 5) if data was acknowledged and this is not the last message
 *    return to 1
 */  
void executeRequest(request_p request)
{
    int numbytes;
    msg_t messageStruct,*message;
    message=&messageStruct;
    
    //set new signal handler for a better death scene
    setSignalHandler(SIGINT,&handleClientStateChange);
    setSignalHandler(SIGTERM,&handleClientStateChange);
    setSignalHandler(SIGHUP,&handleClientStateChange);
    setSignalHandler(SIGPIPE,&handleClientStateChange);
#ifdef comment

    recvfrom perpuse
#endif
    if ((request->serverSocket = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket");
        logMessage("executeRequest: socket error\n");
        exit(1);
    }

    if (addClient(getpid(), request->serverSocket)==FALSE)
    {
    	logMessage("Exiting client.\n");
    	return;
    }

    memset((void*)message,0,sizeof(msg_t));
    do
    {
        if (message->attempt == 0)
        {
        	message = request->modeHandler(request->opcodeHandler(request, message));
        }
        
        if(clientFlag!=ALIVE)
        {
        	//client is dead - no point in continuing
        	break;
        }
        
        if ((numbytes = sendto(request->serverSocket, message, message->size + REPLYHEADERSIZE(), 0,
                               (struct sockaddr *) (request->remoteAddr), sizeof(struct sockaddr))) == -1)
        {
            perror("sendto");
            logMessage("executeRequest: sendto error\n");
            removeClient(getpid());
            close(request->serverSocket);
            break;
        }
        logMessage("executeRequest: sent %d bytes to %s:%d\n", numbytes, inet_ntoa(request->remoteAddr->sin_addr),ntohs(request->remoteAddr->sin_port));
        
        if(clientFlag!=ALIVE)
        {
        	//client is dead - no point in continuing
        	break;
        }
        
        if (isAcknowledger(message))
        {
        	request->acknowledgeHandler(request,message);
        }
    }
    while(clientFlag==ALIVE && MAXBUFLEN==message->size && (message->attempt*getRTT())<getRTO());
    //allow the client to die peacefully
    setSignalHandler(SIGINT, SIG_IGN);
    setSignalHandler(SIGTERM, SIG_IGN);
    setSignalHandler(SIGHUP, SIG_IGN);
    setSignalHandler(SIGPIPE, SIG_IGN);

    removeClient(getpid());
    logMessage("executeRequest: Communication with client %s:%d was ended\n",
    	inet_ntoa(request->remoteAddr->sin_addr),ntohs(request->remoteAddr->sin_port));
}
